<script setup>
import { nextTick, onMounted, onUnmounted, reactive, ref, watchEffect } from "vue";
import { 
  Delete, CirclePlus, Plus, Edit, 
  EditPen, Check, RefreshLeft 
} from "@element-plus/icons-vue";

import { 
  getGroups, deleteGroup, updateGroupName, getPointsByGroup, 
  updatePoint, deletePoint 
} from "@/api/device";
import { PointRealTime } from "@/api/ws";
import CreatePoint from "./CreatePoint.vue";
import NoSelectPage from "@/assets/imgs/noselect.png";
import NothingPage from "@/assets/imgs/nothing.png";

const funcs = ["M", "X", "Y", "D", "W", "L", "R"]

const props = defineProps({
  // 设备编号
  no: {
    type: String,
    default: ""
  }
})

const groups = ref([])
const groupsSelected = ref([])
const groupEdited = ref()
// 展示数据
const displayGroups = ref({})
// websocket监听
let wsSendGroups = []

function toGetGroups(success = () => {}) {
  getGroups(props.no).then(res => {
    if (res.code === 200) {
      groups.value = res.data
      success()
    }
  })
}
// 监听传入的设备编号，不为空时根据设备编号获取设备的点位分组
watchEffect(() => {
  if (props.no) {
    toGetGroups(() => {
      if (groups.value.length > 0) {
        groupsSelected.value = [groups.value[0]]
        getPointsByGroup(props.no, groups.value[0]).then(res => {
          if (res.code === 200) {
            displayGroups.value[groups.value[0]] = res.data
            wsSendGroups = [groups.value[0]]
          }
        })
      } else {
        displayGroups.value = {}
        wsSendGroups = []
      }
    })
  } else {
    groups.value = []
    displayGroups.value = {}
    wsSendGroups = []
  }
})

// 删除分组
function toDeleteGroup(group) {
  ElMessageBox.confirm(`确认删除分组 ${group} 吗？这将会删除该分组下的所有点位，请谨慎操作！`, "警告", {
    type: "warning",
    confirmButtonText: "确定",
    cancelButtonText: "取消"
  }).then(() => {
    deleteGroup(props.no, group).then(res => {
      if (res.code === 200) {
        ElMessage.success("删除成功")
        toGetGroups()
      } else {
        ElMessage.error(res.msg)
      }
    }).catch(err => {
      console.log(err)
      ElMessage.error("网络异常，请稍后重试")
    })
  }).catch(() => {})
}

const createPointVisible = ref(false)

// 新建点位
function toCreatePoint(group) {
  createPointVisible.value = true
  groupEdited.value = group
}

// 创建ws实例用于实时获取点位值
let devCom

onMounted(() => {
  const sendMsg = () => {
    if (wsSendGroups && wsSendGroups.length > 0) {
      devCom.send({ no: props.no, groups: wsSendGroups })
    } else {
      setTimeout(sendMsg, 500)
    }
  }
  devCom = PointRealTime(
    (msg) => {
      for (const group in displayGroups.value) {
        const displayGroup = displayGroups.value[group]
        if (!msg.groups) continue
        const valGroup = msg.groups[group]
        if (!valGroup) continue
        for (let i = 0; i < displayGroup.length; i++) {
          if (!displayGroup[i]) continue
          displayGroup[i].value = valGroup[displayGroup[i].name]
        }
      }
      setTimeout(sendMsg, 500)
    }, 
    sendMsg, 
    () => {
      for (const group in displayGroups.value) {
        const displayGroup = displayGroups.value[group]
        for (let i = 0; i < displayGroup.length; i++) {
          const dev = displayGroup[i]
          if (dev) dev.value = null
        }
      }
    }
  )
})

onUnmounted(() => {
  devCom.close()
})

function collapseChange(activeGroups) {
  // 清除关闭列
  for (const key in displayGroups.value) {
    if (!activeGroups.includes(key)) delete displayGroups.value[key]
  }
  wsSendGroups = activeGroups
  // 查询新展开列的所有点
  for (const group of activeGroups) {
    if (!displayGroups.value[group]) {
      getPointsByGroup(props.no, group).then(res => {
        if (res.code === 200) {
          displayGroups.value[group] = res.data
        }
      })
    }
  }
}

// 拉取有更新的点位表数据更新页面显示
function updateChangeTable(group) {
  if (displayGroups.value[group]) {
    getPointsByGroup(props.no, group).then(res => {
      if (res.code === 200) {
        displayGroups.value[group] = res.data
      }
    })
  }
}

// 删除指定点位
function toDeletePoint(row, group) {
  ElMessageBox.confirm(`确认删除点位 ${row.name} 吗？`, "警告", {
    type: "warning",
    confirmButtonText: "确定",
    cancelButtonText: "取消"
  }).then(() => {
    deletePoint(row.id).then(res => {
      if (res.code === 200) {
        ElMessage.success("删除成功")
        toGetGroups()
        updateChangeTable(group)
      } else {
        ElMessage.error(res.msg)
      }
    }).catch(err => {
      console.log(err)
      ElMessage.error("网络异常，请稍后重试")
    })
  }).catch(() => {})
}

const editGroupName = ref()
const newGroupName = ref()
const groupNameInputRef = ref()
// 修改变量组名称
function toEditGroup(group) {
  if (group === editGroupName.value) {
    toUpdateGroupName()
  } else {
    editGroupName.value = group
    newGroupName.value = group
    nextTick(() => {
      nextTick(() => {
        groupNameInputRef.value[0].input.focus()
      })
    })
  }
}

// 焦点离开输入框时让用户确认是否修改信息
function toUpdateGroupName() {
  if (editGroupName.value === newGroupName.value) {
    editGroupName.value = null
    newGroupName.value = null
    return
  }
  ElMessageBox.confirm(`确认将分组${editGroupName.value} 名称修改为 ${newGroupName.value} 吗？`, "警告", {
    type: "warning",
    confirmButtonText: "修改",
    cancelButtonText: "重置"
  }).then(() => {
    updateGroupName(props.no, editGroupName.value, newGroupName.value).then(res => {
      if (res.code === 200) {
        ElMessage.success("修改成功")
        toGetGroups()   // 拉取新的分组列表
        updateChangeTable(editGroupName.value)    // 更新选中分组的信息
        editGroupName.value = null
        newGroupName.value = null
      } else {
        ElMessage.error(res.msg)
      }
    }).catch(err => {
      console.log(err)
      ElMessage.error("网络异常，请稍后重试")
    })
  }).catch(() => {
    editGroupName.value = null
    newGroupName.value = null
  })
}

const pointEdited = ref()
let editPointCache = ""
// 修改点位信息
function toEditPoint(row) {
  pointEdited.value = row
  editPointCache = JSON.stringify(row)
}

function toUpdatePoint(group) {
  if (editPointCache === JSON.stringify(pointEdited.value)) {
    pointEdited.value = null
    return
  }
  ElMessageBox.confirm(`确认修改点位信息吗？`, "警告", {
    type: "warning",
    confirmButtonText: "确定",
    cancelButtonText: "取消"
  }).then(() => {
    updatePoint(pointEdited.value.id, pointEdited.value).then(res => {
      if (res.code === 200) {
        updateChangeTable(group)    // 更新选中分组的信息
        pointEdited.value = null
        ElMessage.success("修改成功")
      } else {
        ElMessage.error(res.msg)
      }
    }).catch(err => {
      console.log(err)
      ElMessage.error("网络异常，请稍后重试")
    })
  }).catch(() => {})
}

const valueIsNull = (value) => typeof(value) !== 'number' && (!value)
</script>

<template>
  <el-card>
    <template #header>
      <div class="card-header">
        <span>设备点位</span>
        <el-button v-if="props.no"
          text type="primary" :icon="Plus"
          @click="toCreatePoint()"
        >创建点位</el-button>
      </div>
    </template>
    <el-collapse 
      v-if="groups.length > 0"
      v-model="groupsSelected"
      @change="collapseChange"
    >
      <el-collapse-item
        v-for="group in groups" 
        :name="group"
      >
        <template #title>
          <div class="collapse-item-title">
            <div class="btn-group">
              <el-button 
                link :icon="Delete" 
                type="danger" 
                @click.stop="toDeleteGroup(group)"
              />
              <el-button 
                link :icon="CirclePlus" 
                type="primary" 
                @click.stop="toCreatePoint(group)" 
              />
              <el-button 
                link :icon="group === editGroupName ? Check : EditPen" 
                type="primary" 
                @click.stop="toEditGroup(group)"
              />
            </div>
            <div v-if="group === editGroupName">
              <el-input 
                ref="groupNameInputRef"
                v-model="newGroupName" 
                @click.stop
                @blur="toUpdateGroupName()"
              />
            </div>
            <div v-else 
              class="collapse-title" 
              @click.stop="toEditGroup(group)"
            >{{ group }}</div>
          </div>
        </template>
        <el-table :data="displayGroups[group]" max-height="480px" border>
          <el-table-column label="代号" prop="name" width="120px">
            <template #default="{ row }">
              <el-input v-if="pointEdited?.id === row.id" v-model="pointEdited.name" />
              <span v-else>{{ row.name }}</span>
            </template>
          </el-table-column>
          <el-table-column label="功能码" prop="func" width="96px">
            <template #default="{ row }">
              <el-select v-if="pointEdited?.id === row.id" v-model="pointEdited.func">
                <el-option v-for="func in funcs" :label="func" :value="func" />
              </el-select>
              <span v-else>{{ row.func }}</span>
            </template>
          </el-table-column>
          <el-table-column label="地址" prop="addr" width="96px">
            <template #default="{ row }">
              <el-input v-if="pointEdited?.id === row.id" v-model="pointEdited.addr" />
              <span v-else>{{ row.addr }}</span>
            </template>
          </el-table-column>
          <el-table-column label="当前值" prop="value" width="120px">
            <template #default="{ row }">
              <div class="value-box">
                <div :class="valueIsNull(row.value) ? 'inactive-point' : 'active-point'" />
                <el-tag
                  :type="valueIsNull(row.value) ? 'info' : 'success'" 
                  class="value-tag"
                >{{ valueIsNull(row.value) ? '读取失败' : row.value }}</el-tag>
              </div>
            </template>
          </el-table-column>
          <el-table-column label="描述信息" prop="desc" show-overflow-tooltip>
            <template #default="{ row }">
              <el-input v-if="pointEdited?.id === row.id" v-model="pointEdited.desc" />
              <span v-else>{{ row.desc }}</span>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="168" align="center" fixed="right">
            <template #default="{ row }">
              <div v-if="pointEdited?.id === row.id">
                <el-button 
                  link size="small" 
                  type="primary" :icon="Check"
                  @click="toUpdatePoint(group)"
                >确 定</el-button>
                <el-button 
                  link size="small" 
                  type="primary" :icon="RefreshLeft"
                  @click="pointEdited = null"
                >取 消</el-button>
              </div>
              <div v-else>
                <el-button 
                  link size="small" 
                  type="primary" :icon="Edit"
                  @click="toEditPoint(row)"
                >修 改</el-button>
                <el-button 
                  link size="small" 
                  type="danger" :icon="Delete"
                  @click="toDeletePoint(row, group)"
                >删 除</el-button>
              </div>
            </template>
          </el-table-column>
        </el-table>
      </el-collapse-item>
    </el-collapse>
    <el-empty v-else :image="props.no ? NothingPage : NoSelectPage">
      <template #description>
        <span v-if="props.no">该设备未设置任意点位，可
          <el-button link type="primary" @click="toCreatePoint()">点击此处</el-button>
          或右上角创建设备点位
        </span>
        <span v-else>请先点击选择上方任意设备查看信息</span>
      </template>
    </el-empty>
  </el-card>
  <create-point
    :no="props.no"
    :group="groupEdited"
    v-model:visible="createPointVisible" 
    @update="updateChangeTable"
    @update-group="toGetGroups"
  />
</template>

<style lang="scss" scoped>
.collapse-item-title {
  display: flex;
  // justify-content: space-between;
  width: 100%;
  .btn-group {
    margin-left: 12px;
    margin-right: 24px;
  }
  .collapse-title {
    width: 72px;
    font-size: 14px;
  }
}
.value-box {
  display: flex;
  justify-content: center;
  align-items: center;
  .value-tag {
    font-size: 14px;
    width: 72px; 
    display: flex;
    justify-content: flex-end;
  }
}
.active-point {
  position: relative;
  display: inline-block;
  margin-right: 6px;
  height: 8px;
  width: 8px;
  border-radius: 50%;
  background-color: #67c23a;
  &::after {
    content: '';
    position: absolute;
    top: 4px;
    left: 4px;
    transform: translate(-50%, -50%);
    width: 12px;
    height: 12px;
    background-color: #67c23a;
    border-radius: 50%;
    opacity: .35;
    animation: breath .6s linear .5s infinite alternate;
  }
}
.inactive-point {
  position: relative;
  display: inline-block;
  margin-right: 6px;
  height: 8px;
  width: 8px;
  border-radius: 50%;
  background-color: #909399;
  &::after {
    content: '';
    position: absolute;
    top: 4px;
    left: 4px;
    transform: translate(-50%, -50%);
    width: 12px;
    height: 12px;
    background-color: #909399;
    border-radius: 50%;
    opacity: .35;
    animation: breath 1.2s linear .5s infinite alternate;
  }
}
@keyframes breath {
  0%   { opacity: .5; }
  100% { opacity: 0; }
}
</style>
